package cn.kivensoft.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public final class JsonParse {
	/** 解析json字符串成java对象
	 * @param jsonString json格式字符串
	 * @return String/Long/Boolean/Double/Map<String, Object>/List<Object>/null
	 */
	@SuppressWarnings("unchecked")
	public static <T> T from(String jsonString) {
		return jsonString == null || jsonString.isEmpty() ? null : (T) readJsonValue(new StringState(0, jsonString));
	}

	// json解析专用函数
	private static Object readJsonValue(StringState stringState) {
		char c = skipWhiteSpace(stringState);
		if (c == 0)
			return null;

		++stringState.index;
		switch (c) {
			case '{':
				return readJsonAsMap(stringState);
			case '[':
				return readJsonAsList(stringState);
			case '"': case '\'':
				return readJsonAsString(stringState, c);
			default:
				return readJsonAsOther(stringState, c);
		}
	}

	// json解析专用函数
	private static char skipWhiteSpace(StringState stringState) {
		String s = stringState.string;
		for (int i = stringState.index, n = s.length(); i < n; ++i) {
			char c = s.charAt(i);
			if (c != ' ' && c != '\t' && c != '\r' && c != '\n') {
				stringState.index = i;
				return c;
			}
		}
		return 0;
	}

	// json解析专用函数
	private static Map<String, Object> readJsonAsMap(StringState stringState) {
		String s = stringState.string;
		int len = s.length();

		HashMap<String, Object> ret = new HashMap<>();
		boolean comma = true;
		while (stringState.index < len) {
			char c = skipWhiteSpace(stringState);
			if (c == 0)
				throwJsonParseException(stringState.index);
			if (c == '}') {
				++stringState.index;
				return ret;
			} else if (c == ',') {
				if (comma)
					throwJsonParseException(stringState.index);
				comma = true;
				++stringState.index;
			} else if (!comma){
				throwJsonParseException(stringState.index);
			} else {
				// 读取key
				String key;
				if (c == '"' || c == '\'') {
					++stringState.index;
					key = readJsonAsString(stringState, c);
				} else {
					key = readJsonAsString(stringState, (char) 0);
				}

				// 读取value
				char c2 = skipWhiteSpace(stringState);
				if (c2 != ':')
					throwJsonParseException(stringState.index);
				++stringState.index;
				Object value = readJsonValue(stringState);

				ret.put(key, value);
				comma = false;
			}
		}
		return null;
	}

	// json解析专用函数
	private static List<Object> readJsonAsList(StringState stringState) {
		String s = stringState.string;
		int len = s.length();

		ArrayList<Object> ret = new ArrayList<>();
		boolean comma = true;
		while (stringState.index < len) {
			char c = skipWhiteSpace(stringState);
			if (c == 0)
				throwJsonParseException(stringState.index);
			if (c == ']') {
				++stringState.index;
				return ret;
			} else if (c == ',') {
				if (comma)
					throwJsonParseException(stringState.index);
				comma = true;
				++stringState.index;
			} else if (!comma){
				throwJsonParseException(stringState.index);
			} else {
				ret.add(readJsonValue(stringState));
				comma = false;
			}
		}
		return null;
	}

	// json解析专用函数
	private static String readJsonAsString(StringState stringState, char prefix) {
		StringBuilder sb = new StringBuilder();
		String s = stringState.string;
		int idx = stringState.index;
		for (int len = s.length(); idx < len; ++idx) {
			char c = s.charAt(idx);
			if (c == prefix || (prefix == 0 && c == ' ' || c == '\t' || c == '\r' || c == '\n')) {
				++idx;
				break;
			}
			if (c == '\\') {
				if (++idx >= len)
					throwJsonParseException(idx);
				char c2 = s.charAt(idx);
				switch (c2) {
					case '\\':
					case '"' :
					case '\'':
						c = c2;
						break;
					case 'u':
						if (idx + 4 >= len)
							throwJsonParseException(idx);
						c = (char) Integer.parseInt(s.substring(idx + 1, idx + 5), 16);
						idx += 4;
						break;
				}
			}
			sb.append(c);
		}
		stringState.index = idx;
		return sb.toString();
	}

	// json解析专用函数
	private static Object readJsonAsOther(StringState stringState, char prefix) {
		String s = stringState.string;
		int idx = stringState.index, len = s.length();

		if (prefix == 'n' && idx + 2 < len && s.charAt(idx) == 'u'
				&& s.charAt(idx + 1) == 'l' && s.charAt(idx + 2) == 'l') {
			stringState.index += 3;
			return null;
		}

		if (prefix == 't' && idx + 2 < len && s.charAt(idx) == 'r'
				&& s.charAt(idx + 1) == 'u' && s.charAt(idx + 2) == 'e') {
			stringState.index += 3;
			return Boolean.TRUE;
		}

		if (prefix == 'f' && idx + 3 < len && s.charAt(idx) == 'a'
				&& s.charAt(idx + 1) == 'l' && s.charAt(idx + 2) == 's'
				&& s.charAt(idx + 3) == 'e') {
			stringState.index += 4;
			return Boolean.FALSE;
		}

		if (prefix != '-' && prefix != '+' && (prefix < '0' || prefix > '9'))
			throwJsonParseException(idx);

		StringBuilder sb = new StringBuilder();
		sb.append(prefix);

		boolean dot = false;
		for (; idx < len; ++idx) {
			char c = s.charAt(idx);
			if (c == '.') {
				if (!dot) dot = true;
				else throwJsonParseException(idx);
			}
			else if (c < '0' || c > '9') {
				break;
			}
			sb.append(c);
		}
		stringState.index = idx;
		String number = sb.toString();

		Object ret;
		if (dot) ret = new Double(number);
		else ret = new Long(number);
		return ret;
	}

	// json解析专用函数
	private static void throwJsonParseException(int index) {
		throw new RuntimeException(Fmt.fmt("parse json string error at {}", index));
	}

	// json解析专用函数
	static class StringState {
		int index;
		String string;
		StringState(int index, String string) {
			this.index = index;
			this.string = string;
		}
	}
}
